﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Bots.BGBuddy.Helpers;
using CommonBehaviors.Actions;
using GrindBuddy2;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Database;
using Styx.CommonBot.Frames;
using Styx.CommonBot.Profiles;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.World;
using Styx.WoWInternals.WoWObjects;
using System.Windows.Media;
using Action = Styx.TreeSharp.Action;

namespace DruidBuddy
{
    public class DruidBuddy : BotBase
    {
        public override void Initialize()
        {
            Settings.Instance.Load();
            Mount.OnMountUp += Mount_OnMountUp;
        }


        public override Form ConfigurationForm
        {
            get { return new UI(); }
        }

        void Mount_OnMountUp(object sender, MountUpEventArgs e)
        {
            e.Cancel = true;
        }

        public override bool RequiresProfile
        {
            get { return true; }
        }

        #region Properties

        private static Composite _root;

        private bool _shouldHarvest;
        public DruidBuddy()
        {
            LastIndex = 0;
        }

        public List<WoWGameObject> NearByOres
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWGameObject>()
                        .Where(obj =>
                        {
                            _pickupIndoor = (Settings.Instance.PickupInDoor
                                ? obj.IsIndoors || !obj.IsIndoors
                                : !obj.IsIndoors);
                            return obj.SubType == WoWGameObjectType.Chest &&
                                   Settings.Instance.PickupOreList.Contains(obj.Entry) &&
                                   !Blacklist.Contains(obj.Guid, BlacklistFlags.Node) && _pickupIndoor;
                        })
                        .OrderBy(obj => obj.Location.Distance(StyxWoW.Me.Location))
                        .ToList();
            }
        }

        public List<WoWGameObject> NearByHerbs
        {
            get
            {
                return
                    
                    ObjectManager.GetObjectsOfType<WoWGameObject>()
                        .Where(obj =>
                        {
                            _pickupIndoor = (Settings.Instance.PickupInDoor
                                ? obj.IsIndoors || !obj.IsIndoors
                                : !obj.IsIndoors);
                            return obj.SubType == WoWGameObjectType.Chest &&
                                   Settings.Instance.PickupHerbList.Contains(obj.Entry) &&
                                   !Blacklist.Contains(obj.Guid, BlacklistFlags.Node) && _pickupIndoor;
                        })
                        .OrderBy(obj => obj.Location.Distance(StyxWoW.Me.Location))
                        .ToList();
            }
        }
        

        public WoWPlayer NearByPlayer
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWPlayer>()
                        .Where(x => x.Location.Distance(StyxWoW.Me.Location) < 99)
                        .OrderBy(x => StyxWoW.Me.Location.Distance(x.Location))
                        .FirstOrDefault();
            }
        }

        public bool HasResSick
        {
            get { return StyxWoW.Me.Auras.Values.Any(x => x.Name == "Resurrection Sickness"); }
        }

        public bool NeedRepairs
        {
            get { return StyxWoW.Me.DurabilityPercent < 0.2; }
        }

        #endregion

        #region Implemented Members

        public override string Name
        {
            get { return "Druid Buddy v01"; }
        }

        public override Composite Root
        {
            get { return _root ?? (_root = MainRotation()); }
        }

        public override PulseFlags PulseFlags
        {
            get { return PulseFlags.Objects; }
        }

        public int LastIndex { get; set; }

        #endregion

        #region Composites

        private Composite MainRotation()
        {
            return new PrioritySelector(
                new Decorator(ret => StyxWoW.Me.BagsFull, new Action(delegate
            {

            })),
                new Decorator(ret => HasResSick, new ActionAlwaysSucceed()),
                new Decorator(ret => NeedRepairs && (!StyxWoW.Me.IsDead && !StyxWoW.Me.IsGhost), new Action(delegate
                {
                    Repair();
                })),
                new Decorator(ret => StyxWoW.Me.IsDead, new Action(delegate
                {
                    Lua.DoString("RepopMe()");
                })),
                new Decorator(ret => StyxWoW.Me.IsGhost, new Action(
                    delegate
                    {
                        GetCorpse();
                    })),
                new Decorator(ret => StyxWoW.Me.Combat,
                    new PrioritySelector(

            #region Heal

new PrioritySelector(
                // Use the Behavior
                            new Decorator(ctx => RoutineManager.Current.HealBehavior != null,
                                new Sequence(
                                    RoutineManager.Current.HealBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                    )),

                            // Don't use the Behavior
                            new Decorator(ctx => RoutineManager.Current.NeedHeal,
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Healing"),
                                    new Action(ret => RoutineManager.Current.Heal())
                                    ))),

            #endregion

            #region Combat Buffs

 new PrioritySelector(
                // Use the Behavior
                            new Decorator(ctx => RoutineManager.Current.CombatBuffBehavior != null,
                                new Sequence(
                                    RoutineManager.Current.CombatBuffBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                    )
                                ),

                            // Don't use the Behavior
                            new Decorator(ctx => RoutineManager.Current.NeedCombatBuffs,
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Applying Combat Buffs"),
                                    new Action(ret => RoutineManager.Current.CombatBuff())
                                    ))),

            #endregion

            #region Combat

 new PrioritySelector(
                // Use the Behavior
                            new Decorator(ctx => RoutineManager.Current.CombatBehavior != null,
                                new PrioritySelector(
                                    RoutineManager.Current.CombatBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                    )),

                            // Don't use the Behavior
                            new Sequence(
                                new Action(ret => TreeRoot.StatusText = "Combat"),
                                new Action(ret => RoutineManager.Current.Combat())))

            #endregion

)),
                new Decorator(ret => StyxWoW.Me.Mounted && Settings.Instance.Form == Settings.Forms.TravelForm, new Action(delegate
                {
                    Mount.Dismount();
                })),
                new Decorator(ret => _shouldHarvest, new Sequence(
                    new Action(delegate
                    {
                        Logging.Write(Colors.MediumAquamarine, "Found a node.");

                        if (NearByOres.Any())
                        {
                            foreach (var source in NearByOres)
                            {
                                if (GetInCombat()) break;
                                if (!Goto(source))
                                {
                                    Blacklist.Add(source, BlacklistFlags.Node, TimeSpan.FromMinutes(20));
                                    return;
                                }
                                Thread.Sleep(TimeSpan.FromSeconds(1));
                                Logging.Write(Colors.MediumAquamarine, "Picking up: {0}", source.Name);

                                if (BlackList(source)) return;
                                source.Interact();
                                Thread.Sleep(TimeSpan.FromSeconds(5));
                            }
                            ObjectManager.Update();
                        }

                        if (NearByHerbs.Any())
                        {
                            foreach (var source in NearByHerbs)
                            {
                                if (GetInCombat()) break;
                                if (!Goto(source)) return;
                                Thread.Sleep(TimeSpan.FromSeconds(1));
                                Logging.Write(Colors.MediumAquamarine, "Picking up: {0}", source.Name);

                                if (BlackList(source)) return;
                                source.Interact();
                                Thread.Sleep(TimeSpan.FromSeconds(2));
                            }
                            ObjectManager.Update();
                        }

                        _shouldHarvest = false;
                    })
                    )),
                new Decorator(ret => StyxWoW.Me.IsValid, new Action(delegate
                {
                    var closestHotspot =
                        ProfileManager.CurrentProfile.HotspotManager.Hotspots.OrderBy(
                            x => StyxWoW.Me.Location.Distance(x)).FirstOrDefault();
                    
                    for (var i = 0; i < ProfileManager.CurrentProfile.HotspotManager.Hotspots.Count; i++)
                    {
                        if (ProfileManager.CurrentProfile.HotspotManager.Hotspots[i] == closestHotspot)
                        {
                            LastIndex = i + 1;
                            break;
                        }
                    }
                    
                    if (LastIndex == ProfileManager.CurrentProfile.HotspotManager.Hotspots.Count) LastIndex = 0;

                    for (var i = LastIndex; i < ProfileManager.CurrentProfile.HotspotManager.Hotspots.Count; i++)
                    {
                        ObjectManager.Update();

                        if (GetInCombat()) break;

                        if (NearByOres.Any() || NearByHerbs.Any())
                        {
                            _shouldHarvest = true;
                            LastIndex = i;
                            break;
                        }

                        Goto(ProfileManager.CurrentProfile.HotspotManager.Hotspots[i]);
                    }
                }))
                );
        }



        #endregion

        #region Helper Methods
        List<WoWGameObject> nodes = new List<WoWGameObject>();
        private bool BlackList(WoWGameObject source)
        {           
            if (!nodes.Contains(source))
            {
                nodes.Add(source);
                
                if (!w.IsRunning)
                {
                    w = new Stopwatch();
                    w.Start();
                }
            }
            else
            {
                if (w.Elapsed > TimeSpan.FromMinutes(3))
                {
                    Blacklist.Add(source.Guid, BlacklistFlags.Node, TimeSpan.FromMinutes(2));
                    nodes.Remove(source);
                    w.Stop();
                    return true;
                }
            }

            return false;
        }

        public bool Goto(WoWGameObject obj)
        {
            return Goto(obj.Location);
        }

        Stopwatch w = new Stopwatch();
        private bool _pickupIndoor;

        public bool Goto(WoWPoint location, int distance)
        {
            if (!w.IsRunning) w.Start();

            if (Settings.Instance.Form == Settings.Forms.TravelForm)
            {
                if (!TravelFormGoto(location, distance)) return false;
            }

            if (Settings.Instance.Form == Settings.Forms.FlightForm)
            {
                if (!FlightFormGoto(location)) return false;
            }
            

            return true;
        }

        private bool TravelFormGoto(WoWPoint location, int distance)
        {
            var nearestLocationToObject = WoWMathHelper.CalculatePointFrom(StyxWoW.Me.Location, location, distance);
            var pathToDest =
                Navigator.GeneratePath(StyxWoW.Me.Location, nearestLocationToObject);

            if (pathToDest.Count() == 0) return false;

            foreach (var localDest in pathToDest)
            {
                if (!StyxWoW.Me.IsFlying && !Navigator.CanNavigateFully(StyxWoW.Me.Location, localDest, 8192))
                {
                    Logging.Write(Colors.Red, "Cant navigate to the destination.");
                    return false;
                }

                try
                {
                    while (localDest.Distance(StyxWoW.Me.Location) >= 5)
                    {
                        if (GetInCombat()) break;
                        PreventPlayersOrMobs();

                        Navigator.MoveTo(localDest);

                        Thread.Sleep(100);
                    }
                    if (GetInCombat()) break;
                }
                catch (Exception exception)
                {
                    Logging.Write(Colors.Red, exception.Message);
                }
            }

            return true;
        }

        private bool FlightFormGoto(WoWPoint location)
        {
            try
            {
                while (location.Distance(StyxWoW.Me.Location) >= 5)
                {
                    // check playersl
                    Flightor.MoveTo(location, true);
                }
            }
            catch (Exception exception)
            {
                Logging.Write(Colors.Red, exception.Message);
            }

            return true;
        }

 
        public bool Goto(WoWPoint location)
        {
            if (BlackspotManager.IsBlackspotted(location, 20))
                return false;
            var rnd = new Random();
            var randomDistance = rnd.Next(1, 3);
            return Goto(location, randomDistance);
        }


        public bool GetCorpse()
        {
            var result = Goto(StyxWoW.Me.CorpsePoint, 10);
            if (result)
            {
                Thread.Sleep(TimeSpan.FromSeconds(3));
                Lua.DoString("RetrieveCorpse()");
            }
            else
            {
                SpiritRez();
            }

            return result;
        }

        public void Repair()
        {
            WoWPoint location = WoWPoint.Zero;
            var repairNpc = NpcQueries.GetNearestNpc(StyxWoW.Me.FactionTemplate, StyxWoW.Me.MapId, StyxWoW.Me.Location,
                UnitNPCFlags.Repair);

            if (repairNpc == null)
            {
                var vendor = ProfileManager.CurrentProfile.VendorManager.Vendors.FirstOrDefault().FirstOrDefault();

                if (vendor != null)
                    location =
                        vendor.Location;
            }
            else
            {
                location = repairNpc.Location;
            }

            var result = Goto(location);
            if (result)
            {
                var npc = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(x => repairNpc != null && x.Entry == repairNpc.Entry);
                if (npc != null) npc.Interact();

                if (GossipFrame.Instance != null && GossipFrame.Instance.IsVisible &&
                        GossipFrame.Instance.GossipOptionEntries != null)
                {
                    foreach (GossipEntry ge in GossipFrame.Instance.GossipOptionEntries)
                    {
                        if (ge.Type == GossipEntry.GossipEntryType.Vendor)
                        {
                            GossipFrame.Instance.SelectGossipOption(ge.Index);
                            break;
                        }
                    }
                }

                Lua.DoString("RepairAllItems(1)");
            }

        }

        private void SpiritRez()
        {
            // spirit rez
            var spiritHealer =
                ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(obj => obj.IsSpiritHealer);
            if (spiritHealer != null)
            {
                Goto(spiritHealer.Location);
                spiritHealer.Interact();

                Lua.DoString("RunMacroText(\"/click StaticPopup1Button1\")");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Lua.DoString("RunMacroText(\"/click StaticPopup1Button1\")");
            }
        }
        private void PreventPlayersOrMobs()
        {
            ObjectManager.Update();

            var nearbyMob = (from mob in ObjectManager.GetObjectsOfType<WoWUnit>()
                             where mob.IsHostile && !mob.IsDead && !mob.IsFriendly && !mob.IsNeutral && mob.Distance < mob.GetAggroRange(StyxWoW.Me) + 4
                             select mob).FirstOrDefault();

            if (StyxWoW.Me.HasAura("Prowl") && (NearByPlayer != null || nearbyMob != null)) return;

            if (NearByPlayer != null && Settings.Instance.PreventPlayers)
            {
                if (!StyxWoW.Me.IsStealthed && SpellManager.CanCast("Prowl"))
                {
                    Logging.Write(Colors.SkyBlue,
                        "Detected nearby player: {0} Level: {1} Class: {2} Distance {3}. Going stealth.",
                        NearByPlayer.Name, NearByPlayer.Level, NearByPlayer.Class,
                        StyxWoW.Me.Location.Distance(NearByPlayer.Location));

                    SpellManager.Cast("Prowl");
                }
            }
            else if (nearbyMob != null && Settings.Instance.PreventMobs)
            {
                if (!StyxWoW.Me.IsStealthed && SpellManager.CanCast("Prowl"))
                {
                    Logging.Write(Colors.SkyBlue,
                        "Detected nearby mob: {0} Level: {1} Distance {2}. Going stealth.",
                        nearbyMob.Name, nearbyMob.Level,
                        StyxWoW.Me.Location.Distance(nearbyMob.Location));

                    SpellManager.Cast("Prowl");

                    // avoid the mobs pathing direction
                    //while (WoWMathHelper.IsFacing(StyxWoW.Me.Location,3, nearbyMob.Location))
                    //{
                    //    var tempWoWPoint = WoWMathHelper.CalculatePointFrom(StyxWoW.Me.Location, nearbyMob.Location, 10f);
                    //    Goto(tempWoWPoint);
                    //}
                }
            }
            else if (StyxWoW.Me.IsSwimming)
            {
                if (!StyxWoW.Me.HasAura("Aquatic Form"))
                {
                    SpellManager.Cast("Aquatic Form");
                }
            }
            else
            {
                if (!StyxWoW.Me.HasAura("Travel Form"))
                {
                    SpellManager.CastSpellById(783);
                }
            }
        }

        private bool GetInCombat()
        {
            if(Settings.Instance.NoCombat && !_shouldHarvest) return false;

            if (StyxWoW.Me.Combat)
            {
                var nearbyMobs =
                    ObjectManager.GetObjectsOfType<WoWUnit>()
                        .Where(obj => obj.Aggro && obj.Attackable && !obj.Elite && obj.IsHostile).ToList();

                SpellManager.Cast(nearbyMobs.Count >= 3 ? "Bear Form" : "Cat Form");

                return true;
            }
            return false;
        }

        

        
        #endregion
    }
}
